#include "SimulationManager.h"
#include <cstdlib>

//Update physics of the simulation
//Input is timestep.
void SimulationManager::_DefaultSMUpdate( float ts ){}


bool SimulationManager::_DefaultSMInitWindow(int w, int h) {
	//Must initialize GLFW first
	if (!glfwInit() ) {
		return false;
	}
	
	if (!glfwOpenWindow(w, h, 8, 8, 8, 8, 24, 8, GLFW_WINDOW)) {
		glfwTerminate();
		return false;
	}

	return true;
}

void SimulationManager::_DefaultSMInitFrames() {	
	glLoadMatrixf( &(
		(mCam.GetMatrix( PROJECTION_MATRIX ) * 
		mCam.GetMatrix( MODELVIEW_MATRIX ) )
		[0][0]) );

	glMatrixMode(GL_MODELVIEW_MATRIX);

	glClearColor(1.0, 1.0, 1.0, 1.0);
	glClear(GL_COLOR_BUFFER_BIT);
}


//Display code
void SimulationManager::_DefaultSMDisplay() {
	glClearColor(1.0, 1.0, 1.0, 1.0);
	glClear(GL_COLOR_BUFFER_BIT);
}

void SimulationManager::_DefaultSMEndFrames() {
	glfwSwapBuffers();
}

//Class constructor
SimulationManager::SimulationManager(int w, int h) 
	: mTS (1.0f/30.0f),
	  mWindowW(w),
	  mWindowH(h),
	  mCam(mWindowW, mWindowH),
	  InitWindow(NULL),
	  InitFrames(NULL),
	  Update(NULL),
	  Display(NULL),
	  mIsRunning(false),
	  mIsPaused(false)
{
	InitInputCallbacks();

	SetCallbackInitWindow(NULL);
	SetCallbackInitFrames(NULL);
	SetCallbackUpdate(NULL);
	SetCallbackDisplay(NULL);
	SetCallbackEndFrames(NULL);
}

//Class deconstructor
SimulationManager::~SimulationManager() {
}

//Run the simulation
int SimulationManager::Run(){
	InitWindow == NULL ? 
		this->_DefaultSMInitWindow(mWindowW, mWindowH) :
		InitWindow() ;

	MainLoop();

	return EXIT_SUCCESS;
}

//The main loop for the simulation.
void SimulationManager::MainLoop() {

	glfwSetTime(0.0);
	double timeSinceLastUpdate = 0;

	mIsRunning = true;

	while (mIsRunning) {
		if (!mIsPaused) {
			timeSinceLastUpdate = glfwGetTime();
			//Update state of simulation
			if (IsTimeToUpdate(timeSinceLastUpdate)) {
				Update == NULL ? 
					_DefaultSMUpdate(mTS) :
					Update(mTS);
			}
		}
		//Perform the user-defined InitFrames callback.
		InitFrames == NULL ?
			_DefaultSMInitFrames() :
			InitFrames();

		//Perform the user-defined Display callback.
		Display == NULL ?
			_DefaultSMDisplay() :
			Display();

		EndFrames == NULL ?
			_DefaultSMEndFrames() :
			EndFrames();
	}
}

//Input is the time since the last update step.
bool SimulationManager::IsTimeToUpdate( double t) {
	if (t > mTS) {
		glfwSetTime(t - mTS);
		return true;
	}

	return false;
}


//Can Do something different here.
void SimulationManager::InitInputCallbacks() {
	glfwSetMousePosCallback(MouseMoveCallback);

	glfwSetMouseButtonCallback(MouseButtonCallback);

	glfwSetCharCallback(KeyboardCallback);
}

void SimulationManager::Quit() {
	mIsRunning = false;
}

DCamera & SimulationManager::GetCamera() {
	return mCam;
}